/*******************************************************************************
 * Copyright (c) 2001-2003 Boris Pruessmann and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Common Public License v0.5 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.resources;

import com.google.common.collect.Lists;

import java.util.List;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IPerforceServerManager;
import net.sourceforge.perforce.core.internal.P4TeamProvider;
import net.sourceforge.perforce.core.syncinfo.ResourceSyncInfo;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.core.RepositoryProvider;

/**
 * @version $Revision: 488 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class LocalFile extends LocalResource {
  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the ResourceSyncInfo. */
  private ResourceSyncInfo syncInfo;

  // --------------------------------------------------------------- Public
  // Static Factory Methods

  /**
   * Call this method to create a LocalFile instance.
   * 
   * @param server the perforce server.
   * @param resource the local resource.
   * @return the LocalFile instance.
   */
  public static final LocalFile create(IPerforceServer server, IResource resource) {
    return new LocalFile(server, resource);
  }

  //----------------------------------------------------------------------------
  // ---- Constructors

  /**
   * Constructor for LocalFile.
   * 
   * @see LocalResource#LocalResource(IPerforceServer, IResource)
   */
  private LocalFile(IPerforceServer server, IResource resource) {
    super(server, resource);
  }

  // -----------------------------------------------------------------------
  // Public Static Methods

  /**
   * Returns an array of filenames of the given files.
   * 
   * @param files the files.
   * @return the filename array.
   */
  public static String[] fileNames(LocalFile[] files) {
    List<String> argumentList = Lists.newArrayList();
    for (int i = 0, length = files.length; i < length; i++) {
      argumentList.add(files[i].getAbsolutePath());
    }

    return argumentList.toArray(new String[argumentList.size()]);
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  /**
   * Call this method to retrieve the name of the file.
   * 
   * @return the name of the file.
   */
  public String getName() {
    return getLocation().lastSegment();
  }

  /**
   * Returns the ResourceSyncInfo for the file.
   * 
   * @return the ResourceSyncInfo.
   */
  @Override
  public ResourceSyncInfo getSyncInfo() {
    trace("start getSyncInfo for LocalFile %s \n executed by thread %s", String.valueOf(this
        .toString()), Thread.currentThread().getName());
    try {

      if (syncInfo == null) {
        try {
          IResource resource = toResource();
          if (resource != null && resource.exists()) {
            syncInfo = PerforceCorePlugin.getSynchronizer().getResourceSync(resource);
            if (PerforceCorePlugin.getPlugin().getShouldRefreshWhenNotFound()) {
              ResourceSyncInfo[] infos =
                  getServer().fstat(resource.getLocation().toOSString(), null);
              if (infos != null && infos.length == 1) {
                syncInfo = infos[0];
              }
            }
          } else {
            ResourceSyncInfo[] infos = getServer().fstat(getLocation().toOSString(), null);
            if (infos != null && infos.length == 1) {
              syncInfo = infos[0];
            }
          }
        } catch (PerforceException e) {
          PerforceCorePlugin.log(e.getStatus());
        }

        if (syncInfo == null) {
          syncInfo = ResourceSyncInfo.NULL_INFO;
        }
      }

      return syncInfo;
    } finally {
      trace("end getSyncInfo");
    }
  }

  /**
   * Checks whether the resource has a corresponding resource object.
   * 
   * @return true if there is a remote object.
   */
  @Override
  public boolean hasRemote() {
    ResourceSyncInfo syncInfo = getSyncInfo();
    return !syncInfo.isNull() && !ResourceSyncInfo.ACTION_DELETE.equals(syncInfo.getAction());
  }

  /**
   * Checks whether file is checked out locally.
   * 
   * @return true if file is checked out locally.
   */
  @Override
  public boolean isCheckedOut() {
    ResourceSyncInfo syncInfo = getSyncInfo();
    return !syncInfo.isNull()
        && !(syncInfo.getAction().equals("") || ResourceSyncInfo.ACTION_DELETE.equals(syncInfo
            .getAction()));
  }

  @Override
  public boolean isResolved() {
    ResourceSyncInfo syncInfo = getSyncInfo();
    if (syncInfo == null) {
      return true;
    }

    return syncInfo.isResolved();
  }


  /**
   * Checks whether the file is dirty.
   * 
   * @return true if file is dirty.
   * 
   *         TODO: Check whether we can use modification date.
   */
  @Override
  public boolean isDirty() {
    return isCheckedOut();
  }

  /**
   * Checks whether there is any newer remote object.
   * 
   * @return true if there is a more recent remote object, false otherwise.
   */
  @Override
  public boolean isOutOfDate() {
    ResourceSyncInfo syncInfo = getSyncInfo();

    boolean result = false;
    if (syncInfo != null) {
      result = !syncInfo.isResolved();
      result =
          result
              || (syncInfo.getHaveRev() != syncInfo.getHeadRev() && !syncInfo.getHeadAction()
                  .equals(ResourceSyncInfo.ACTION_DELETE));
    }

    return result;
  }

  /**
   * Reverts the file.
   * 
   * @throws PerforceException in case of an error.
   */
  public void revert() throws PerforceException {
    IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
    IPerforceServer server = manager.getPerforceServer(toResource());

    server.revert(new String[] {getSyncInfo().getClientFileName()}, null);

    IResource resource = toResource();
    if (resource != null) {
      P4TeamProvider provider =
          (P4TeamProvider) RepositoryProvider.getProvider(resource.getProject(), PerforceCorePlugin
              .getTypeId());

      try {
        resource.refreshLocal(IResource.DEPTH_ONE, null);
        provider.refreshState(new IResource[] {resource}, IResource.DEPTH_ONE, null);
      } catch (CoreException e) {
        throw new PerforceException(e.getStatus());
      }
    }
  }

  /**
   * Opens the file for delete.
   * 
   * @throws PerforceException in case of an error.
   */
  public void delete() throws PerforceException {
    IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
    IPerforceServer server = manager.getPerforceServer(toResource());

    server.delete(new String[] {getSyncInfo().getClientFileName()});
  }

  /**
   * Syncs the file.
   * 
   * @param revision the revision to sync to.
   * @param monitor for feedback.
   * @return the status of the operation.
   * 
   * @throws PerforceException in case of an error.
   */
  public IStatus sync(String revision, IProgressMonitor monitor) throws PerforceException {
    IPerforceServerManager manager = PerforceCorePlugin.getServerManager();
    IPerforceServer server = manager.getPerforceServer(toResource());

    String spec = getSyncInfo().getClientFileName();
    return server.sync(spec, revision, monitor);
  }

  @Override
  public String toString() {
    // TODO: Check whether the depot filename is more appropriate for dialogs
    return getLocation().toOSString();
  }

  @Override
  public boolean equals(Object object) {
    if (object == this) {
      return true;
    }

    if (!(object instanceof LocalFile)) {
      return false;
    }

    LocalFile other = (LocalFile) object;
    return getLocation().equals(other.getLocation());
  }

  @Override
  public int hashCode() {
    return getLocation().hashCode();
  }

  @Override
  public boolean isCheckedOutForAdd() {
    ResourceSyncInfo syncInfo = getSyncInfo();
    return !syncInfo.isNull() && syncInfo.getAction().equals(ResourceSyncInfo.ACTION_ADD);
  }

  @Override
  public boolean isCheckedOutForDelete() {
    ResourceSyncInfo syncInfo = getSyncInfo();
    return !syncInfo.isNull() && syncInfo.getAction().equals(ResourceSyncInfo.ACTION_DELETE);
  }
}
